package org.lcx.pipeline.spring;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.ManagedMap;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import org.lcx.pipeline.Processor;
import org.lcx.pipeline.bean.Transition;
import org.lcx.pipeline.impl.PipelineChain;
import org.lcx.pipeline.impl.PipelineLink;
import org.lcx.pipeline.impl.PipelineManagerImpl;

/**
 * pipeline自定义spring标签定义<br>
 * 解析xml配置
 */
public class PipelineManagerBeanParser implements BeanDefinitionParser {

	private final Class<?> parserBeanClass;
	private final boolean required;

	public PipelineManagerBeanParser(Class<?> _beanClass, boolean _required) {
		this.parserBeanClass = _beanClass;
		this.required = _required;
	}

	public BeanDefinition parse(Element element, ParserContext parserContext) {
		return parse(element, parserContext, parserBeanClass, required);
	}

	private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass,
			boolean required) {
		RootBeanDefinition beanDefinition = new RootBeanDefinition();
		beanDefinition.setBeanClass(beanClass);
		beanDefinition.setLazyInit(false);
		String id = element.getAttribute("id");
		if ((id == null || id.length() == 0) && required) {
			String generatedBeanName = element.getAttribute("name");
			if (generatedBeanName == null || generatedBeanName.length() == 0) {
				if (PipelineManagerImpl.class.equals(beanClass)) {
					generatedBeanName = "PipeLineManagerBean";
				} else if (PipelineChain.class.equals(beanClass)) {
					generatedBeanName = "PipelineChainBean";
				} else {
					generatedBeanName = element.getAttribute("interface");
				}
			}
			if (generatedBeanName == null || generatedBeanName.length() == 0) {
				generatedBeanName = beanClass.getName();
			}
			id = generatedBeanName;
			int counter = 2;
			while (parserContext.getRegistry().containsBeanDefinition(id)) {
				id = generatedBeanName + (counter++);
			}
		}
		if (id != null && id.length() > 0) {
			if (parserContext.getRegistry().containsBeanDefinition(id)) {
				throw new IllegalStateException("Duplicate spring bean id " + id);
			}
			parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
			beanDefinition.getPropertyValues().addPropertyValue("id", id);
		}

		Set<String> props = new HashSet<String>();
		for (Method setter : beanClass.getMethods()) {
			String name = setter.getName();
			if (name.length() > 3 && name.startsWith("set") && Modifier.isPublic(setter.getModifiers())
					&& setter.getParameterTypes().length == 1) {
				Class<?> type = setter.getParameterTypes()[0];
				String property = name.substring(3, 4).toLowerCase() + name.substring(4);
				props.add(property);
				Method getter = null;
				try {
					getter = beanClass.getMethod("get" + name.substring(3), new Class<?>[0]);
				} catch (NoSuchMethodException e) {
					try {
						getter = beanClass.getMethod("is" + name.substring(3), new Class<?>[0]);
					} catch (NoSuchMethodException e2) {
					}
				}
				if (getter == null || !Modifier.isPublic(getter.getModifiers())
						|| !type.equals(getter.getReturnType())) {
					continue;
				}
				// 获取spring的xml中针对bean定义的属性的值
				String value = element.getAttribute(property);
				if (value != null && !"".equals(value)) {
					value = value.trim();
					if (value.length() > 0) {
						Object reference = null;
					    if (isPrimitive(type)) { // 基础数据类型的处理
							if ("async".equals(property) && "false".equals(value)
									|| "timeout".equals(property) && "0".equals(value)
									|| "delay".equals(property) && "0".equals(value)
									|| "version".equals(property) && "0.0.0".equals(value)
									|| "stat".equals(property) && "-1".equals(value)
									|| "reliable".equals(property) && "false".equals(value)) {
								// 兼容旧版本xsd中的default值
								value = null;
							}
							reference = value;
						} else {
							if ("ref".equals(property) && parserContext.getRegistry().containsBeanDefinition(value)) {
								BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);
								if (!refBean.isSingleton()) {
									throw new IllegalStateException(
											"The exported service ref " + value + " must be singleton! Please set the "
													+ value + " bean scope to singleton, eg: <bean id=\"" + value
													+ "\" scope=\"singleton\" ...>");
								}
							}
							// ref 是指处理bean的参考模式
							reference = new RuntimeBeanReference(value);
						}
						// 给要返回bean进行设置，然后即将返回了
						beanDefinition.getPropertyValues().addPropertyValue(property, reference);
					}
				}else{
					// 处理事务
					if ("transactionManager".equals(property)) {
						Object reference = new RuntimeBeanReference("transactionManager");
						beanDefinition.getPropertyValues().addPropertyValue(property, reference);
					}
				}
			}
		}

		// 以上代码是处理了pipelineManager的基础四个属性，现在开始处理pipelineManager的重要属性pipelineChains
		parsePipelineChains(id, element.getChildNodes(), beanDefinition, parserContext);

		return beanDefinition;
	}

	private static void parsePipelineChains(String id, NodeList nodeList, RootBeanDefinition beanDefinition,
			ParserContext parserContext) {
		if (nodeList != null && nodeList.getLength() > 0) {
			ManagedMap pipelineChains = null;
			ManagedMap pipelineLinks = null;
			int i = 0;

			while (true) {
				if (i >= nodeList.getLength()) {
					if (pipelineChains != null) {
						beanDefinition.getPropertyValues().addPropertyValue("pipelineChains", pipelineChains);
					}
					if (pipelineLinks != null) {
						beanDefinition.getPropertyValues().addPropertyValue("pipelineLinks", pipelineLinks);
					}
					break;
				}

				Node node = nodeList.item(i);
				if (node instanceof Element) {
					Element element = (Element) node;
					if ("pipelineChain".equals(node.getNodeName()) || "pipelineChain".equals(node.getLocalName())) {
						String pipelineChainName = element.getAttribute("name");
						if (pipelineChainName == null || pipelineChainName.length() == 0) {
							throw new IllegalStateException("<pipe:pipelineChain> name attribute == null");
						}

						if (pipelineChains == null) {
							pipelineChains = new ManagedMap();
						}

						// 递归调主parse方法进行对pipelineChain的解析
						BeanDefinition pipelineChainBeanDefinition = parse((Element) node, parserContext,
								PipelineChain.class, true);

						pipelineChains.put(new TypedStringValue(pipelineChainName), pipelineChainBeanDefinition);
					} else if ("pipelineLink".equals(node.getNodeName())
							|| "pipelineLink".equals(node.getLocalName())) {
						String pipelineLinkName = element.getAttribute("name");
						if (pipelineLinkName == null || pipelineLinkName.length() == 0) {
							throw new IllegalStateException("<pipe:pipelineLink> name attribute == null");
						}

						if (pipelineLinks == null) {
							pipelineLinks = new ManagedMap();
						}

						// 递归调主parse方法进行对pipelineLink的解析
						BeanDefinition pipelineLinkBeanDefinition = parse((Element) node, parserContext,
								PipelineLink.class, true);

						// 调用parseNested解析pipelineLink下的processor与transition
						parseNested((Element) node, parserContext, Processor.class, true, "processor", "transition",
								pipelineLinkBeanDefinition);

						pipelineLinks.put(new TypedStringValue(pipelineLinkName), pipelineLinkBeanDefinition);
					}
				}

				++i;
			}
		}

	}

	private static void parseNested(Element element, ParserContext parserContext, Class<?> beanClass, boolean required,
			String tag, String property, BeanDefinition beanDefinition) {
		NodeList nodeList = element.getChildNodes();
		if (nodeList != null && nodeList.getLength() > 0) {
			boolean first = true;
			ManagedMap transitions = null;

			for (int i = 0; i < nodeList.getLength(); ++i) {
				Object reference;
				Node node = nodeList.item(i);
				// tag = "processor"
				if (node instanceof Element && (tag.equals(node.getNodeName()) || tag.equals(node.getLocalName()))) {
					Element subElement = (Element) node;
					// 根据xsd定义first必然是processor
					if (first) {
						first = false;
						String value = subElement.getAttribute("ref");
						BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);
						if (!refBean.isSingleton()) {
							throw new IllegalStateException(
									"The exported service ref " + value + " must be singleton! Please set the " + value
											+ " bean scope to singleton, eg: <bean id=\"" + value
											+ "\" scope=\"singleton\" ...>");
						}
						// ref 是指处理bean的参考模式
						reference = new RuntimeBeanReference(value);
						// 将processor的bean赋值给pipelinelink
						beanDefinition.getPropertyValues().addPropertyValue("processor", reference);
					}
				} else if (node instanceof Element
						&& (property.equals(node.getNodeName()) || property.equals(node.getLocalName()))) {
					// 此处处理transition
					if (transitions == null) {
						transitions = new ManagedMap();
						beanDefinition.getPropertyValues().addPropertyValue("transitions", transitions);
					}
					// 递归调主parse方法进行对pipelineLink的解析
					// BeanDefinition transitionBeanDefinition =
					// parseTransition((Element) node, parserContext,
					// Transition.class, true);
					parseTransition((Element) node, parserContext, Transition.class, true, transitions);
				}
			}
		}

	}

	private static BeanDefinition parseTransition(Element element, ParserContext parserContext, Class<?> beanClass,
			boolean required, ManagedMap transitions) {
		RootBeanDefinition beanDefinition = new RootBeanDefinition();
		beanDefinition.setBeanClass(beanClass);
		beanDefinition.setLazyInit(false);
		String returnValue = element.getAttribute("returnValue");
		String link = element.getAttribute("link");
		beanDefinition.getPropertyValues().addPropertyValue("returnValue", returnValue);
		beanDefinition.getPropertyValues().addPropertyValue("link", link);
		transitions.put(new TypedStringValue(returnValue), beanDefinition);
		return beanDefinition;
	}

	private static boolean isPrimitive(Class<?> cls) {
		return cls.isPrimitive() || cls == Boolean.class || cls == Byte.class || cls == Character.class
				|| cls == Short.class || cls == Integer.class || cls == Long.class || cls == Float.class
				|| cls == Double.class || cls == String.class || cls == Date.class || cls == Class.class;
	}

}
